import os
import sys
import commands
import ConfigParser
import datetime

# This is the unit type dictionary.  This dictionary will tell us
# how to handle the option it has been given.
# Available units: bytes, kilobytes, megabytes, gigabytes, pages, 
#                  text, int
# Available Types: single, pair, array
unitdict = {
  # Memory Options
      'kmemsize': {'unit': 'bytes',     'type': 'pair'},
   'lockedpages': {'unit': 'pages',     'type': 'pair'},
   'privvmpages': {'unit': 'pages',     'type': 'pair'},
      'shmpages': {'unit': 'pages',     'type': 'pair'},
     'phsypages': {'unit': 'text',      'type': 'single'},
   'vmguarpages': {'unit': 'pages',     'type': 'pair'},
  'oomguarpages': {'unit': 'pages',     'type': 'pair'},
  
  # Network Options
    'numtcpsock': {'unit': 'int',       'type': 'pair'},
     'tcpsndbuf': {'unit': 'int',       'type': 'pair'},
     'tcprcvbuf': {'unit': 'int',       'type': 'pair'},
  'othersockbuf': {'unit': 'int',       'type': 'pair'},
   'dgramrcvbuf': {'unit': 'int',       'type': 'pair'},
  'numothersock': {'unit': 'int',       'type': 'pair'},
    'ip_address': {'unit': 'text',      'type': 'array'},
    'nameserver': {'unit': 'text',      'type': 'array'},
  'searchdomain': {'unit': 'text',      'type': 'array'},
  
  # Disk Options
    'dcachesize': {'unit': 'int',       'type': 'pair'},
     'diskspace': {'unit': 'kilobytes', 'type': 'pair'},
    'diskinodes': {'unit': 'int',       'type': 'pair'},
'quotaugidlimit': {'unit': 'int',       'type': 'single'},

  # Priority Options
        'ioprio': {'unit': 'int',       'type': 'single'},
     'quotatime': {'unit': 'int',       'type': 'single'},
   'cpupriority': {'unit': 'int',       'type': 'single'},
  
  # CPU Options
       'numcpus': {'unit': 'int',       'type': 'single'},
      'cpulimit': {'unit': 'int',       'type': 'single'},
  
  # Resourse Limitations
        'numpty': {'unit': 'int',       'type': 'pair'},
    'numsiginfo': {'unit': 'int',       'type': 'pair'},
       'numfile': {'unit': 'int',       'type': 'pair'},
     'numflocks': {'unit': 'int',       'type': 'pair'},
     'avnumproc': {'unit': 'int',       'type': 'pair'},
       'numproc': {'unit': 'int',       'type': 'pair'},
     'numiptent': {'unit': 'int',       'type': 'pair'},
    
  # Other Options
       'vpsroot': {'unit': 'text',      'type': 'single'},
    'vpsprivate': {'unit': 'text',      'type': 'single'},
    'ostemplate': {'unit': 'text',      'type': 'single'},
 'origin_sample': {'unit': 'text',      'type': 'single'},
      'hostname': {'unit': 'text',      'type': 'single'},
          'name': {'unit': 'text',      'type': 'single'}
}

# This is the configuration class.  Basically a single place for defining how
# we interract with the configuration file.
class ConfigFile(object):
  def __init__(self, filename='/etc/ovzmanager.conf'):
    self.config = ConfigParser.RawConfigParser()
    self.config.read(filename) 
  def get(self, stanza, option):
    return config.get(stanva, option).split(',')

# This is the initialization of the config file.  If we need it anywhere, we
# just need to specify that we want the global config.
config = ConfigFile()

class LDAPObject(object):
  def __init__(self):
    global config
    self.server = config.get('LDAP', 'Server')
    self.basedn = config.get('LDAP', 'Base DN')
    self.filter = config.get('LDAP', 'Search Filter')
  
  def getUserByHost(self, host):
    pass
  
  def getGroups(self):
    pass
  
  def getUsers(self):
    pass
  
  def updateUser(self):
    pass
  
  def updateGroup(self):
    pass
  
  def getDNS(self, search, type):
    pass

# This is the base class for an OpenVZ option.  We are trying to intelegently
# pull the values into the object.  I am open to suggestions as to better ways
# to handle this.
class BeanCounterObject(object):
  def __init__(self, value, option):
    global unitdict
    self.option = option
    self.unit   = unitdict[option]['unit']
    self.type   = unitdict[option]['type']
    self.value  = None
    self.limit  = None
    self.set(value)
  
  def set(value, unit=self.unit, vtype=self.type):
    # This can probably be cleaned up, however this should work for now ;)
    if vtype == 'single':
      if unit == 'text':
        self.value = value
      elif unit == 'int':
        self.value = int(value)
      else:
        self.value = convert(value, orig=unit, ret='kilobytes')
    elif vtype == 'pair':
      if unit == 'text':
        self.value, self.limit = value.split(':')
      elif unit == 'int':
        self.value = int(self.value.split(':')[0])
        self.limit = int(self.value.split(':')[1])
      else:
        self.value = convert(value.split(':')[0], orig=unit, ret='kilobytes')
        self.limit = convert(value.split(':')[1], orig=unit, ret='kilobytes')
    elif vtype == 'array':
      if unit == 'text':
        self.value = value.split()
      elif unit == 'int':
        self.value = []
        for item in value.split():
          self.value.append(int(item))
      else:
        self.value = []
        for item in value.split()
          self.value.append(convert(value.split(':')[0], orig=unit, ret='kilobytes'))
  
  # This function will return the OpenVZ-valid value for this parameter. 
  # Basically reconvert the values if needed and spit them out in the unit 
  # that was specified during initialization.
  def show():
    values = []
    if self.units == 'none':
      values.append(self.value)
      if self.type == 'double':
        values.append(self.limit)
    else:
      values.append(convert(self.barrier, orig='kilobytes', ret=self.units))
      if self.type == 'double':
        values.append(convert(self.limit, orig='kilobytes', ret=self.units))
        
    return ':'.join(values)
  
  # This function is an extension of the show function.  It simply adds the
  # --parameter to the OpenVZ-valid values.  The idea here is to make it easy
  # to apply changes using vzctl by simply using the return values of this
  # function.  I came up with this idea after truing to build the applyConfig()
  # function in the OpenVZ class.  Trust me when I say that the initial idea
  # for that function was utter shit.
  def setting():
    if self.option != 'none':
      return '--%s \'%s\'' % (self.option, self.show())

class VZNode(object):
  def __init__(self, veid):
    global config
    configFile            = '%s/%s.conf' % (veid, config.get('OpenVZ', 'VPS Config Location'))
    self.veid             = int(veid)
    
    # Below is the parsing of the various options for a VPS container.
    # I know most of these aren't often set however in the interest of
    # trying to be a completionist I included everything in the example
    # OVZ container in /example_data/ovz_cugnet_200.conf.  If you would
    # like more information as to what each of these individual options
    # do, goto the links below:
    #  http://wiki.openvz.org/UBC_primary_parameters
    #  http://wiki.openvz.org/UBC_secondary_parameters
    #  http://wiki.openvz.org/UBC_auxiliary_parameters
    #
    # As a side note, because the OpenVZ parameter names are completely
    # retarded, I tried to use variable names that, although longer,
    # properly convey the context of what is going on.  I still do not
    # know if breaking out the soft and hard limits for each item is
    # the best way to handle this, and I may switch at a later point to
    # simply providing a list for each, or even both.
    self.kernelmemory     = None
    self.lockedpages      = None
    self.privlegedmemory  = None
    self.sharedmemory     = None
    self.physicalpages    = None
    self.burstmemory      = None
    self.guarenteedmemory = None
    self.numtcpsockets    = None
    self.numptys          = None
    self.numsiginfo       = None
    self.tcpsendbuffer    = None
    self.tcpreceivebuffer = None
    self.udpsendbuffer    = None
    self.udpreceivebuffer = None
    self.numothersockets  = None
    self.diskcache        = None
    self.numopenfiles     = None
    self.numfilelocks     = None
    self.numprocesses     = None
    self.numipfilters     = None
    self.diskspace        = None
    self.diskinodes       = None
    self.userquotas       = None
    self.diskpriority     = None
    self.quotatime        = None
    self.cpupriority      = None
    self.numcpus          = None
    self.cpulimit         = None
    self.vpsroot          = None
    self.vpsprivate       = None
    self.ostemplate       = None
    self.origintemplate   = None
    self.ip               = None
    self.nameserver       = None
    self.domain           = None
    self.hostname         = None
    self.name             = None
    self.diskquotas       = None
    
    # This block here prevents us from trying to get the configuration of a
    # reserved system VEID.  This way we are only pulling what we consider
    # "good" VPS configurations.  We will still create the object as a
    # placeholder however.  I don't know where this will come in handy, but
    # you never know.
    if veid <= 100:
      self.reserved = True
      self.user     = 'system'
    else:
      self.reserved = False
      self.user     = self.getUser()
      self.getDefaultVPSConfig()
    self.getVPSConfig(veid)
  
  def getDefaultVPSConfig():
    self.kernelmemory     = BeanCounterObject(config.get('OpenVZ Defaults','Kernel Memory'),      'VALUE')
    self.lockedpages      = BeanCounterObject(config.get('OpenVZ Defaults','Locked Pages'),       'VALUE')
    self.privlegedmemory  = BeanCounterObject(config.get('OpenVZ Defaults','Privledged Memory'),  'VALUE')
    self.sharedmemory     = BeanCounterObject(config.get('OpenVZ Defaults','Shared Memory'),      'VALUE')
    self.physicalpages    = BeanCounterObject(config.get('OpenVZ Defaults','Physical Pages'),     'VALUE')
    self.burstmemory      = BeanCounterObject(config.get('OpenVZ Defaults','Burst Memory'),       'VALUE')
    self.guarenteedmemory = BeanCounterObject(config.get('OpenVZ Defaults','Guarenteed Memory'),  'VALUE')
    self.numtcpsockets    = BeanCounterObject(config.get('OpenVZ Defaults','Num TCP Sockets'),    'VALUE')
    self.numptys          = BeanCounterObject(config.get('OpenVZ Defaults','Num Terminals'),      'VALUE')
    self.numsiginfo       = BeanCounterObject(config.get('OpenVZ Defaults','Num Sig Info'),       'VALUE')
    self.tcpsendbuffer    = BeanCounterObject(config.get('OpenVZ Defaults','TCP Send Buffer'),    'VALUE')
    self.tcpreceivebuffer = BeanCounterObject(config.get('OpenVZ Defaults','TCP Receive Buffer'), 'VALUE')
    self.udpsendbuffer    = BeanCounterObject(config.get('OpenVZ Defaults','UDP Send Buffer'),    'VALUE')
    self.udpreceivebuffer = BeanCounterObject(config.get('OpenVZ Defaults','UDP Receive Buffer'), 'VALUE')
    self.numothersockets  = BeanCounterObject(config.get('OpenVZ Defaults','Num Other Sockets'),  'VALUE')
    self.diskcache        = BeanCounterObject(config.get('OpenVZ Defaults','Disk Cache'),         'VALUE')
    self.numopenfiles     = BeanCounterObject(config.get('OpenVZ Defaults','Num Open Files'),     'VALUE')
    self.numfilelocks     = BeanCounterObject(config.get('OpenVZ Defaults','Num File Locks'),     'VALUE')
    self.numprocesses     = BeanCounterObject(config.get('OpenVZ Defaults','Num Processes'),      'VALUE')
    self.numipfilters     = BeanCounterObject(config.get('OpenVZ Defaults','Num IP Filters'),     'VALUE')
    self.diskspace        = BeanCounterObject(config.get('OpenVZ Defaults','Disk Space'),         'VALUE')
    self.diskinodes       = BeanCounterObject(config.get('OpenVZ Defaults','Disk Inodes'),        'VALUE')
    self.userquotas       = BeanCounterObject(config.get('OpenVZ Defaults','User Quotas'),        'VALUE')
    self.diskpriority     = BeanCounterObject(config.get('OpenVZ Defaults','Disk Priority'),      'VALUE')
    self.quotatime        = BeanCounterObject(config.get('OpenVZ Defaults','Quota Time'),         'VALUE')
    self.cpupriority      = BeanCounterObject(config.get('OpenVZ Defaults','CPU Priority'),       'VALUE')
    self.numcpus          = BeanCounterObject(config.get('OpenVZ Defaults','Num CPUs'),           'VALUE')
    self.cpulimit         = BeanCounterObject(config.get('OpenVZ Defaults','CPU Limit'),          'VALUE')
    self.vpsroot          = BeanCounterObject(config.get('OpenVZ Defaults','VPS Root'),           'VALUE')
    self.vpsprivate       = BeanCounterObject(config.get('OpenVZ Defaults','VPS Private'),        'VALUE')
    self.ostemplate       = BeanCounterObject(config.get('OpenVZ Defaults','OS Template'),        'VALUE')
    self.origintemplate   = BeanCounterObject(config.get('OpenVZ Defaults','Origin Template'),    'VALUE')
    self.ip               = BeanCounterObject(config.get('OpenVZ Defaults','IP'),                 'VALUE')
    self.nameserver       = BeanCounterObject(config.get('OpenVZ Defaults','Name Server'),        'VALUE')
    self.domain           = BeanCounterObject(config.get('OpenVZ Defaults','Domain'),             'VALUE')
    self.hostname         = BeanCounterObject(config.get('OpenVZ Defaults','Hostname'),           'VALUE')
    self.name             = BeanCounterObject(config.get('OpenVZ Defaults','Name'),               'VALUE')
    self.diskquotas       = BeanCounterObject(config.get('OpenVZ Defaults','Disk Quotas'),        'VALUE')
  
  # This function will get the configuration of the VPS from the OpenVZ
  # configuration file.
  def getVPSConfig(self, veid, filename=self.configfile):
    configfile = open(filename)
    for line in configfile:
      # First we need to check and make sure that the line really is a value
      # pair.  Otherwise we will just skip passed it.
      if len(line.split('=').strip("\"")) == 2:
        option, value = line.split('=').strip("\"")
        option = option.lower()
        if   option == 'kmemsize':        self.kernelmemory     = BeanCounterObject(value,option)
        elif option == 'lockedpages':     self.lockedpages      = BeanCounterObject(value,option)
        elif option == 'privvmpages':     self.privlegedmemory  = BeanCounterObject(value,option)
        elif option == 'shmpages':        self.sharedmemory     = BeanCounterObject(value,option)
        elif option == 'physpages':       self.physicalpages    = BeanCounterObject([0,'MAX_ULONG'],option)
        elif option == 'vmguarpages':     self.burstmemory      = BeanCounterObject(value,option)
        elif option == 'oomguarpages':    self.guarenteedmemory = BeanCounterObject(value,option)
        elif option == 'numtcpsock':      self.numtcpsockets    = BeanCounterObject(value,option)
        elif option == 'numpty':          self.numptys          = BeanCounterObject(value,option)
        elif option == 'numsiginfo':      self.numsiginfo       = BeanCounterObject(value,option)
        elif option == 'tcpsndbuf':       self.tcpsendbuffer    = BeanCounterObject(value,option)
        elif option == 'tcprsvbuf':       self.tcpreceivebuffer = BeanCounterObject(value,option)
        elif option == 'othersuckbuf':    self.udpsendbuffer    = BeanCounterObject(value,option)
        elif option == 'dgramrcvbuf':     self.udpreceivebuffer = BeanCounterObject(value,option)
        elif option == 'numothersock':    self.numothersockets  = BeanCounterObject(value,option)
        elif option == 'dcachesize':      self.diskcache        = BeanCounterObject(value,option)
        elif option == 'numfile':         self.numopenfiles     = BeanCounterObject(value,option)
        elif option == 'numflocks':       self.numfilelocks     = BeanCounterObject(value,option)
        elif option == 'avnumproc':       self.numprocesses     = BeanCounterObject(value,option)
        elif option == 'numiptent':       self.numipfilters     = BeanCounterObject(value,option)
        elif option == 'diskspace':       self.diskspace        = BeanCounterObject(value,option)
        elif option == 'diskinodes':      self.diskinodes       = BeanCounterObject(value,option)
        elif option == 'quotaugidlimit':  self.userquotas       = BeanCounterObject(value,option)
        elif option == 'ioprio':          self.diskpriority     = BeanCounterObject(value,option)
        elif option == 'quotatime':       self.quotatime        = BeanCounterObject(value,option)
        elif option == 'cpuunits':        self.cpupriority      = BeanCounterObject(value,option)
        elif option == 'cpus':            self.numcpus          = BeanCounterObject(value,option)
        elif option == 'cpulimit':        self.cpulimit         = BeanCounterObject(value,option)
        elif option == 've_root':         self.vpsroot          = BeanCounterObject(value,option)
        elif option == 've_private':      self.vpsprivate       = BeanCounterObject(value,option)
        elif option == 'ostemplate':      self.ostemplate       = BeanCounterObject(value,option)
        elif option == 'origin_sample':   self.origintemplate   = BeanCounterObject(value,option)
        elif option == 'ip_address':      self.ip               = BeanCounterObject(value,option)
        elif option == 'nameserver':      self.nameserver       = BeanCounterObject(value,option)
        elif option == 'searchdomain':    self.domain           = BeanCounterObject(value,option)
        elif option == 'hostname':        self.hostname         = BeanCounterObject(value,option)
        elif option == 'name':            self.name             = BeanCounterObject(value,option)
        
        # Disk Quota Master Switch
        if option == 'disk_quota':
          if value.lower() == 'no':
            self.diskquotas               = False
          else:
            self.diskquotas               = True
          
  def prettyPrint(self, outputtype):
    if outputtype == 'simple':
      output = '\n'.join(['%20s : %s' % ('Node Name'        , self.name),
                          '%20s : %d' % ('VEID'             , self.veid),
                          '%20s : %s' % ('Owning User'      , self.user),
                          '%20s : %d' % ('Guarenteed Memory', self.guarenteedmemory),
                          '%20s : %d' % ('Burstable Memory' , self.burstmemory),
                          '%20s : %d' % ('Disk Space'       , self.diskspace),
                          '%20s : %s' % ('Status'           , self.status())
                         ])
    
    return output
  
  # This function will apply any changes in the configuration of the node.
  # Essentially it will perform a massive vzctl set --save over every option
  # it can modify.  The idea here is that you can make changes in the config
  # here before applying them.  This allows you to fix any mistakes before you
  # set anything not nice ;)
  def applyChanges(self):
    settings = []
    
    os.system('vzctl set %s %s' % (self.veid, settings))
  
  # Commands after these point are simply wrappers around vzctl.  Eventually
  # an attempt will be made to make these changes using a more native way,
  # however I do not see that comming for a long time yet.
  
  # Start the VZNode
  def start(self):
    os.system('vzctl start %s' % self.veid)
  
  # Stop the VZNode
  def stop(self):
    os.system('vzctl stop %s' % self.veid)
  
  # Restart the VZNode
  def restart(self):
    os.system('vzctl restart %s' % self.veid)
  
  # Create the VZNode
  def 
  
  
# This function will conver from any of the units below to any of the
# units below.  By default it will conver from pages to megabytes.  If
# you want to convert different units, simply specify the orig and ret
# variables.  The orig value would denote the origin, or source unit.  
# The ret variable would denote the return, or destination unit.
# 
# Available Units
# ---------------
# pages, bytes, kilobytes, megabytes, gigabytes
#
def convert(self, values, orig='pages', ret='kilobytes'):
  
  # What this dictionary is for is to normalize everything to bytes.
  # Using bytes as a base point keeps the math simple.
  units = {
              'bytes': 1,
              'pages': 256,
              'kilobytes': 1024,
              'megabytes': (1024 * 1024),
              'gigabytes': (1024 * 1024 * 1024),
            }
  return int(float(value) * units[orig] / units[ret])

def getNodes():
  pass
    

### Original PoC code past this point.  This code will be replaced!!!
def vzlist():
  orig = commands.getoutput('vzlist -a')
  ret_val = []
  
  for line in orig.split('\n'):
    node = {
                'veid': line.strip().split()[0],
               'nproc': line.strip().split()[1],
              'status': line.strip().split()[2],
              'pri_ip': line.strip().split()[3],
            'hostname': line.strip().split()[4]
           }
    if node['veid'] == 'VEID':
      continue
    else:
      ret_val.append(node)
  return ret_val

def get_user(node):
  ldapsearch = commands.getoutput('ldapsearch -h 172.16.0.12 -LLL -x -b "dc=cugnet,dc=local" host=' + node)
  ret_val = 'none'
  
  for line in ldapsearch.split('\n'):
    if line == "":
      continue
    else:
      values = line.strip().split()
      if values[0] == 'uid:':
        ret_val = values[1]
  return ret_val

def get_conf_opt(search_option, veid):
  config = open('/etc/vz/conf/' + veid + '.conf', 'r')
  ret_val = 'none'
  
  for line in config:
    if line.find('#') != -1 or line.find('=') == -1:
      continue    
    option = line.split('=')[0]
    value = line.split('=')[1].split('"')[1]
    
    if option == search_option.upper():
      ret_val = value
  return ret_val